﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Linq.Expressions;
using Ceyhun.ExpressionTrees;

namespace ExpressionTreeTests
{
    public partial class MainWindow : Window
    {
        System.Linq.Expressions.Expression lastExpr = null;
        string lastRepresentation = null;

        public MainWindow()
        {
            InitializeComponent();

            this.KeyDown += (o, e) => { if (e.Key == Key.Escape) this.Close(); };

            Action redisplay = () => { 
                if (lastExpr != null && lastRepresentation != null) Display(lastExpr, lastRepresentation); };

            TopToBottomButton.Click += (o, e) => redisplay();
            BottomToTopButton.Click += (o, e) => redisplay();
            LeftToRightButton.Click += (o, e) => redisplay();
            RightToLeftButton.Click += (o, e) => redisplay();
        }

        private void Display(System.Linq.Expressions.Expression expr) { Display(expr, expr.ToString()); }

        private void Display(System.Linq.Expressions.Expression expr, string representation)
        {
            ExpressionTreeGrid.Children.Clear();
            GraphDirection dir = GraphDirection.TopToBottom;
            if (TopToBottomButton.IsChecked == true)
                dir = GraphDirection.TopToBottom;
            else if (BottomToTopButton.IsChecked == true)
                dir = GraphDirection.BottomToTop;
            else if (LeftToRightButton.IsChecked == true)
                dir = GraphDirection.LeftToRight;
            else if (RightToLeftButton.IsChecked == true)
                dir = GraphDirection.RightToLeft;
            else
                dir = GraphDirection.TopToBottom;
            lastExpr = expr;
            lastRepresentation = representation;
            Canvas canvas = ExpressionTree.Display(expr, dir);
            ExpressionText.Text = representation;
            var viewer = new ScrollViewer
            {
                HorizontalScrollBarVisibility = ScrollBarVisibility.Auto,
                VerticalScrollBarVisibility = ScrollBarVisibility.Auto
            };
            viewer.Content = canvas;
            ExpressionTreeGrid.Children.Add(viewer);
        }

        private void mnuArithmeticExpressions_Click(object sender, RoutedEventArgs e)
        {
            Expression<Func<int, int>> expr = n => n + 1 - n * 2 / 4;
            Display(expr, "n => n + 1 - n * 2 / 4");
        }

        private void mnuCheckedExpressions_Click(object sender, RoutedEventArgs e)
        {
            Expression<Func<int, int>> expr = n => checked(n+1) + checked(n-1) + checked(n*2);
            Display(expr, "n => checked(n + 1) + checked(n - 1) + checked(n * 2)");
        }

        private void mnuPowerAndModuloExpressions_Click(object sender, RoutedEventArgs e)
        {
            Expression<Func<int, int>> expr = n => n % 2;
            Display(expr, "n => n % 2");
        }

        private void mnuBitwiseOperations_Click(object sender, RoutedEventArgs e)
        {
            Expression<Func<int, int>> expr = n => n & 3 | n ^ 2;
            Display(expr, "n => n & 3 | n ^ 2");
        }

        private void mnuShiftOperations_Click(object sender, RoutedEventArgs e)
        {
            Expression<Func<int, int>> expr = n => n << 3 >> 2;
            Display(expr, "n => n << 3 >> 2");
        }

        private void mnuConditionalBooleanOperations_Click(object sender, RoutedEventArgs e)
        {
            Expression<Func<int, bool>> expr = n => n > 10 && n < 5 || n > 20;
            Display(expr, "n => n > 10 && n < 5 || n > 20");
        }

        private void mnuComparisonOperations_Click(object sender, RoutedEventArgs e)
        {
            Expression<Func<int, bool>> expr = n => n > 0 && n >= 2 || n == n || n < 10 && n <= 5 && n != 4;
            Display(expr, "n => n > 0 && n >= 2 || n == n || n < 10 && n <= 5 && n != 4");
        }

        private void mnuCoalescingOperations_Click(object sender, RoutedEventArgs e)
        {
            Expression<Func<object, object, object>> expr = (obj1, obj2) => obj1 ?? obj2;
            Display(expr, "(obj1, obj2) => obj1 ?? obj2");
        }

        private void mnuArrayIndexingOperations_Click(object sender, RoutedEventArgs e)
        {
            Expression<Func<int[], int>> expr = a => a[0];
            Display(expr, "a => a[0]");
        }

        private void mnuConditionalExpression_Click(object sender, RoutedEventArgs e)
        {
            Expression<Func<int, int, int>> expr = (a,b) => a > b ? a : b;
            Display(expr, "(a,b) => a > b ? a : b");
        }

        private void mnuConstantExpression_Click(object sender, RoutedEventArgs e)
        {
            Expression<Func<int>> expr = () => 42;
            Display(expr, "() => 42");
        }

        private void mnuInvocationExpression_Click(object sender, RoutedEventArgs e)
        {
            Func<double, double> f = x => Math.Sqrt(x);
            Expression<Func<double, bool>> expr = x => f(x) == x;
            Display(expr, "x => f(x) == x (Fixed-point function)");
        }

        private void mnuLambdaExpression_Click(object sender, RoutedEventArgs e)
        {
            Expression<Func<int, int>> expr = x => 10 * ((Func<int, int>)(y => y * 2))(x);
            Display(expr, "x => 10 * ((Func<int, int>)(y => y * 2))(x)");
        }

        private void mnuListInitialization_Click(object sender, RoutedEventArgs e)
        {
            Expression<Func<List<int>>> expr = () => new List<int> { 2, 3 };
            Display(expr, "() => new List<int> { 2, 3 }");
        }

        private void mnuFieldAccess_Click(object sender, RoutedEventArgs e)
        {
            Expression<Func<DateTime>> expr = () => DateTime.MinValue;
            Display(expr, "() => DateTime.MinValue");
        }

        private void mnuPropertyAccess_Click(object sender, RoutedEventArgs e)
        {
            Expression<Func<DateTime>> expr = () => DateTime.Now;
            Display(expr, "() => DateTime.Now");
        }

        private void mnuNewExpression_Click(object sender, RoutedEventArgs e)
        {
            Expression<Func<Window>> expr = () => new Window();
            Display(expr, "() => new Window()");
        }

        private void mnuMethodCall_Click(object sender, RoutedEventArgs e)
        {
            Expression<Func<string>> expr = () => 1.ToString();
            Display(expr, "() => 1.ToString()");
        }

        private void mnuMemberInitialization_Click(object sender, RoutedEventArgs e)
        {
            Expression<Func<Button>> expr = () => new Button { Content = "OK", Width = 100 };
            Display(expr, "() => new Button { Content = \"OK\", Width = 100 }");
        }

        private void mnuParameterExpression_Click(object sender, RoutedEventArgs e)
        {
            Expression<Func<int,int>> expr = (n) => n;
            Display(expr, "(n) => n");
        }

        private void mnuConvertExpression_Click(object sender, RoutedEventArgs e)
        {
            Expression<Func<int, int>> expr = (n) => (int)n;
            Display(expr, "(n) => (int)n");
        }

        private void mnuConvertChecked_Click(object sender, RoutedEventArgs e)
        {
            Expression<Func<int, int>> expr = (n) => checked((int)n);
            Display(expr, "(n) => checked((int)n)");
        }

        private void mnuNewArrayBounds_Click(object sender, RoutedEventArgs e)
        {
            Expression<Func<int, int[]>> expr = (n) => new int[n];
            Display(expr, "(n) => new int[n]");
        }

        private void mnuNewArrayInit_Click(object sender, RoutedEventArgs e)
        {
            Expression<Func<int, int[]>> expr = (n) => new int[] { n, n+1 };
            Display(expr, "(n) => new int[] { n, n+1 }");
        }

        private void mnuArrayLength_Click(object sender, RoutedEventArgs e)
        {
            Expression<Func<int, int>> expr = (n) => (new int[n+1]).Length * 2;
            Display(expr, "(n) => (new int[n+1]).Length * 2");
        }

        private void mnuNegate_Click(object sender, RoutedEventArgs e)
        {
            Expression<Func<int, int>> expr = (n) => -n * 2 - 1;
            Display(expr, "(n) => -n * 2 - 1");
        }

        private void mnuNegateChecked_Click(object sender, RoutedEventArgs e)
        {
            Expression<Func<int, int>> expr = (n) => checked(-n * 2) - 1;
            Display(expr, "(n) => checked(-n * 2) - 1");
        }

        private void mnuNotExpression_Click(object sender, RoutedEventArgs e)
        {
            Expression<Func<bool, bool, bool>> expr = (p, q) => !(p && q);
            Display(expr, "(p, q) => !(p && q)");
        }

        private void mnuTypeAs_Click(object sender, RoutedEventArgs e)
        {
            Expression<Func<int, int?>> expr = (n) => n as int?;
            Display(expr, "(n) => n as int?");
        }

        private void mnuTypeBinaryExpression_Click(object sender, RoutedEventArgs e)
        {
            Expression<Func<bool>> expr = () => 42 is int;
            Display(expr, "() => 42 is int");
        }

        private void mnuSomeExpression_Click(object sender, RoutedEventArgs e)
        {
            Expression<Func<int>> expr = () => 42;
            Display(expr, expr.ToString() + " (for experimenting with)");
        }
    }
}
